/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.openide.loaders; import java.io.IOException; import java.io.ObjectInputStream; import java.util.Enumeration; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Iterator; import javax.swing.event.ChangeListener; import javax.swing.event.ChangeEvent; import org.openide.filesystems.*; import org.openide.util.RequestProcessor; import org.openide.util.enum.ArrayEnumeration; import org.openide.util.enum.FilterEnumeration; import org.openide.util.enum.SingletonEnumeration; import org.openide.util.enum.SequenceEnumeration; import org.openide.util.actions.SystemAction; import org.openide.util.NbBundle; /** Pool of data loaders. * Provides access to set of registered * {@link DataLoader loaders} in the system. They are used to find valid data objects * for given files. * <P> * The default instance can be retrieved with * {@link org.openide.TopManager#getLoaderPool}. * * @author Jaroslav Tulach, Petr Hamernik, Dafe Simonek */ public abstract class DataLoaderPool extends Object implements java.io.Serializable { /** SUID */ static final long serialVersionUID=-360141823874889956L; /** standard system loaders. Accessed by getSystemLoaders method only */ private static DataLoader[] systemLoaders; /** standard default loaders. Accessed by getDefaultLoaders method only */ private static DataLoader[] defaultLoaders; /** Cache of loaders for faster toArray() method. */ private transient DataLoader[] loaderArray; /** maps class names of loaders to the loaders (String, DataLoader) */ private transient HashMap map; /** List of listeners (ChangeListener) * @associates ChangeListener*/ private transient HashSet listeners; /** set of listeners (OperationListener) * @associates OperationListener*/ private transient HashSet operations; /** prefered loader */ private transient DataLoader preferredLoader; /** Create new loader pool. */ protected DataLoaderPool () { } /** Create new loader pool and set preferred loader. * The preferred loader will be asked before any other to recognize files (also before the system * loader). * * @param loader the preferred loader */ protected DataLoaderPool (DataLoader loader) { preferredLoader = loader; } /** Get an enumeration of data loaders. * Must be overridden in subclasses to provide a list of additional loaders. * The list should <em>not</em> include the preferred loader. * * @return enumeration of {@link DataLoader}s */ protected abstract Enumeration loaders (); /** Add a new listener to the listener list. A listener is notified of * any change which was made to the loader pool (add, remove, or reorder). * * @param chl new listener */ public final synchronized void addChangeListener (ChangeListener chl) { if (listeners == null) listeners = new HashSet(); listeners.add(chl); } /** Remove a listener from the listener list. * * @param chl listener to remove */ public final synchronized void removeChangeListener (ChangeListener chl) { if (listeners == null) return; listeners.remove(chl); } // Clears loaderArray before firing a change. /** Fire change event to all listeners. Asynchronously. * @param che change event */ protected final void fireChangeEvent (final ChangeEvent che) { if (listeners == null) return; loaderArray = null; map = null; HashSet cloned; // clone listener list synchronized (this) { cloned = (HashSet)listeners.clone(); } // fire on cloned list to prevent from modifications when firing Iterator iter = cloned.iterator(); while (iter.hasNext()) { final ChangeListener l = (ChangeListener)iter.next(); // separates the task to small pieces not to slow down the // rest of the IDE RequestProcessor.postRequest (new Runnable () { public void run () { l.stateChanged(che); } }); } } /** Add a listener for operations on data objects. * @param l the listener */ public synchronized final void addOperationListener (OperationListener l) { if (operations == null) operations = new HashSet (); operations.add (l); } /** Remove a listener for operations on data objects. * @param l the listener */ public synchronized final void removeOperationListener (OperationListener l) { if (operations != null) { operations.remove (l); } } /** Fires operation event to all listeners. * Clears loaderArray before firing a change. * @param ev event to fire * @param type the type of the event */ final void fireOperationEvent (OperationEvent ev, int type) { if (operations == null) return; HashSet cloned; // clone listener list synchronized (this) { cloned = (HashSet)operations.clone(); } // fire on cloned list to prevent from modifications when firing for (Iterator iter = cloned.iterator(); iter.hasNext(); ) { OperationListener l = (OperationListener)iter.next (); switch (type) { case OperationEvent.COPY: l.operationCopy ((OperationEvent.Copy)ev); break; case OperationEvent.MOVE: l.operationMove ((OperationEvent.Move)ev); break; case OperationEvent.DELETE: l.operationDelete (ev); break; case OperationEvent.RENAME: l.operationRename ((OperationEvent.Rename)ev); break; case OperationEvent.SHADOW: l.operationCreateShadow ((OperationEvent.Copy)ev); break; case OperationEvent.TEMPL: l.operationCreateFromTemplate ((OperationEvent.Copy)ev); break; case OperationEvent.CREATE: l.operationPostCreate (ev); break; } } } /** Get an enumeration of all loaders, including the preferred and system loaders. * This should be the list of loaders as actually used by the system. * Typically it will consist of, in this order: * <ol> * <li>The preferred loader, if any. * <li>The system loaders, such as may be used for folders, shadows, etc. * <li>Module-specified loaders. * <li>The loader for instance data objects. * <li>Default loaders, which may handle files not otherwise recognizable. * </ol> * Applications should not rely on the exact contents of the pool, * rather the fact that this contains all the loaders which are * capable of recognizing files in the order in which they are * called. * @return enumeration of {@link DataLoader}s */ public final Enumeration allLoaders () { // enumeration of systemloaders followed by normal loaders Enumeration en =new SequenceEnumeration ( new SequenceEnumeration ( new ArrayEnumeration (getSystemLoaders ()), loaders () ), new ArrayEnumeration (getDefaultLoaders ()) ); if (preferredLoader != null) { // prepends preferred loader Enumeration queue = new SingletonEnumeration (preferredLoader); return new SequenceEnumeration (queue, en); } else { // enumeration without pref. loader return en; } } /** Get an array of loaders that are currently registered. * Does not include special system loaders, etc. * @return array of loaders * @see #loaders */ public DataLoader[] toArray () { DataLoader[] localArray = loaderArray; if (localArray != null) return localArray; ArrayList loaders = new ArrayList (); Enumeration en = loaders (); while (en.hasMoreElements ()) { loaders.add(en.nextElement ()); } localArray = new DataLoader[loaders.size()]; localArray = (DataLoader[])loaders.toArray(localArray); loaderArray = localArray; return localArray; } /** Finds the first producer of a representation class. * Scans through the list of all loaders and returns the first one * whose representation class is a superclass of <code>clazz</code>. * * @param clazz class to find producer for * @return data loader or <CODE>null</CODE> if there is no loader that * can produce the class */ public final DataLoader firstProducerOf (Class clazz) { Enumeration en = allLoaders (); while (en.hasMoreElements ()) { DataLoader dl = (DataLoader)en.nextElement (); if (dl.getRepresentationClass ().isAssignableFrom (clazz)) { // representation class is super class of clazz return dl; } } return null; } /** Get an enumeration of all producers of a representation class. * @see #firstProducerOf * * @param clazz class to find producers for * @return enumeration of {@link DataLoader}s */ public final Enumeration producersOf (final Class clazz) { return new FilterEnumeration (allLoaders ()) { /** Accepts only those loaders that produces superclass of clazz */ public boolean accept (Object o) { DataLoader dl = (DataLoader)o; return clazz.isAssignableFrom( dl.getRepresentationClass() ); } }; } /** private class for next method. Empty implementation of * DataLoaderRecognized. */ private static final DataLoader.RecognizedFiles emptyDataLoaderRecognized = new DataLoader.RecognizedFiles () { /** No op. replacement. * * @param fo file object to exclude */ public void markRecognized (FileObject fo) { } }; /** Find a data object for this file object. * All loaders are asked to recognize it according to their priority. * <p><em>Note:</em> normally you are looking for the data object, whether one had been created already * or not; so do not use this, rather use {@link DataObject#find}! * @param fo file object to recognize * @return the data object for this object or <CODE>null</CODE> if * no loader recognizes this file * @exception DataObjectExistsException if the object for this primary file * already exists * @exception IOException if the data object is recognized but * an error occurs during instantiation * @see #findDataObject(FileObject, DataLoader.RecognizedFiles) */ public DataObject findDataObject (FileObject fo) throws IOException { return findDataObject (fo, emptyDataLoaderRecognized); } /** Find a data object for this file object, considering already-recognized files. * First of all looks at the * file extended attribute <code>NetBeansDataLoader</code>; if it is set and it * contains the class name of a valid {@link DataLoader}, that loader is given preference. * For all loaders used, the first to return non-<code>null</code> from {@link DataLoader#findDataObject} * is used. * <p><em>Note:</em> normally you are looking for the data object, whether one had been created already * or not; so do not use this, rather use {@link DataObject#find}! * * @param fo file object to recognize * @param r recognized files buffer * @return the data object for this object * @exception DataObjectExistsException if the object for this primary file * already exists * @exception IOException if the data object is recognized but * an error occurs during instantiation */ public DataObject findDataObject ( FileObject fo, DataLoader.RecognizedFiles r ) throws IOException { // try to find assigned loader String assignedLoaderName = (String)fo.getAttribute (DataObject.EA_ASSIGNED_LOADER); if (assignedLoaderName != null) { DataLoader l = getAssignedDataLoader(assignedLoaderName); if (l != null) { DataObject obj = l.findDataObject (fo, r); if (obj != null) { // notify it fireOperationEvent (new OperationEvent (obj), OperationEvent.CREATE); // file has been recognized return obj; } } } // scan through loaders java.util.Enumeration en = allLoaders (); while (en.hasMoreElements ()) { DataLoader l = (DataLoader)en.nextElement (); DataObject obj = l.findDataObject (fo, r); if (obj != null) { // the loader recognized the file // notify it fireOperationEvent (new OperationEvent (obj), OperationEvent.CREATE); return obj; } } return null; } /** Lazy getter for system loaders. */ private static DataLoader[] getSystemLoaders () { if (systemLoaders == null) { systemLoaders = new DataLoader [] { new FolderLoader (), new ShadowLoader () }; } return systemLoaders; } /** Lazy getter for default loaders. */ private static DataLoader[] getDefaultLoaders () { if (defaultLoaders == null) { defaultLoaders = new DataLoader [] { new XMLDataObject.Loader (), new InstanceLoader (), new DefaultLoader () }; } return defaultLoaders; } /** Getter for default file loader * @return the default file loader */ static DataLoader getDefaultFileLoader () { return getDefaultLoaders ()[2]; } /** Getter for folder loader * @return the folder loader */ static DataLoader getFolderLoader () { return getSystemLoaders ()[0]; } /** Getter for shadow loader. */ static DataLoader getShadowLoader () { return getSystemLoaders ()[1]; } /** Creates map that maps names of loaders to loaders in the pool. * @return map (String, DataLoader) */ private HashMap getMap () { HashMap m = map; if (m == null) { m = new HashMap (); Enumeration en = allLoaders (); while (en.hasMoreElements ()) { Object o = en.nextElement (); // adds name of class and the loader m.put (o.getClass ().getName (), o); } map = m; } return m; } /** Gets assigned loader. * @param name - name of the loader * @return data loader */ private DataLoader getAssignedDataLoader(final String name) { Map map = getMap(); DataLoader loader = (DataLoader)map.get (name); if (loader == null) { // patch reflecting change of packages String newName = org.openide.util.Utilities.translate(name); loader = (DataLoader)map.get(newName); } return loader; } // // Default loaders // /* Loader for folders */ /** Public only for serialization. */ public static class FolderLoader extends DataLoader { /** Default set of actions on the DataFolder. */ private static SystemAction[] defaultFolderActions = new SystemAction[] { SystemAction.get (org.openide.actions.OpenLocalExplorerAction.class), SystemAction.get (org.openide.actions.FindAction.class), SystemAction.get (org.openide.actions.FileSystemAction.class), null, SystemAction.get (org.openide.actions.CompileAction.class), SystemAction.get (org.openide.actions.CompileAllAction.class), null, SystemAction.get (org.openide.actions.BuildAction.class), SystemAction.get (org.openide.actions.BuildAllAction.class), null, SystemAction.get (org.openide.actions.CutAction.class), SystemAction.get (org.openide.actions.CopyAction.class), SystemAction.get (org.openide.actions.PasteAction.class), null, SystemAction.get (org.openide.actions.DeleteAction.class), SystemAction.get (org.openide.actions.RenameAction.class), null, // JST: there should be a template Package (DataFolder) instead of this action SystemAction.get (org.openide.actions.NewAction.class), SystemAction.get (org.openide.actions.NewTemplateAction.class), null, SystemAction.get (org.openide.actions.ToolsAction.class), SystemAction.get (org.openide.actions.PropertiesAction.class) }; static final long serialVersionUID =-8325525104047820255L; /* Representation class is DataFolder */ public FolderLoader () { super (DataFolder.class); } protected void initialize () { setDisplayName (NbBundle.getBundle (DataLoaderPool.class).getString ("LBL_folder_loader_display_name")); setActions (defaultFolderActions); } protected DataObject handleFindDataObject ( FileObject fo, DataLoader.RecognizedFiles recognized ) throws IOException { if (fo.isFolder ()) { return new DataFolder (fo); } else { return null; } } } /* Instance loader to recognize instances. */ /** Public only for serialization. */ public static class InstanceLoader extends UniFileLoader { static final long serialVersionUID =-3462727693843631328L; /* Creates new InstanceLoader */ public InstanceLoader() { super(InstanceDataObject.class); } protected void initialize () { setDisplayName (NbBundle.getBundle (DataLoaderPool.class).getString ("LBL_instance_loader_display_name")); ExtensionList ext = new ExtensionList(); ext.addExtension ("instance"); // NOI18N setExtensions(ext); setActions(new SystemAction[] { SystemAction.get (org.openide.actions.CustomizeBeanAction.class), null, SystemAction.get (org.openide.actions.FileSystemAction.class), null, SystemAction.get(org.openide.actions.CutAction.class), SystemAction.get(org.openide.actions.CopyAction.class), SystemAction.get(org.openide.actions.PasteAction.class), null, SystemAction.get(org.openide.actions.DeleteAction.class), // SystemAction.get(RenameAction.class), null, SystemAction.get (org.openide.actions.ToolsAction.class), SystemAction.get(org.openide.actions.PropertiesAction.class) }); } /* Creates the right data object for given primary file. * It is guaranteed that the provided file is realy primary file * returned from the method findPrimaryFile. * * @param primaryFile the primary file * @return the data object for this file * @exception DataObjectExistsException if the primary file already has data object */ protected MultiDataObject createMultiObject (FileObject primaryFile) throws DataObjectExistsException, java.io.IOException { return new InstanceDataObject(primaryFile, this); } } /* Loader for file objsects not recognized by any other loader */ /** Public only for serialization. */ public static class DefaultLoader extends DataLoader { /** Default set of actions on the DefaultDataObject. */ private static SystemAction[] defaultDataActions = new SystemAction[] { SystemAction.get (org.openide.actions.FileSystemAction.class), null, SystemAction.get (org.openide.actions.CutAction.class), SystemAction.get (org.openide.actions.CopyAction.class), SystemAction.get (org.openide.actions.PasteAction.class), null, SystemAction.get (org.openide.actions.DeleteAction.class), SystemAction.get (org.openide.actions.RenameAction.class), null, SystemAction.get (org.openide.actions.ToolsAction.class), SystemAction.get (org.openide.actions.PropertiesAction.class) }; static final long serialVersionUID =-6761887227412396555L; /* Representation class is DataFolder */ public DefaultLoader () { super (DefaultDataObject.class); } protected void initialize () { setDisplayName (NbBundle.getBundle (DataLoaderPool.class).getString ("LBL_default_loader_display_name")); setActions (defaultDataActions); } protected DataObject handleFindDataObject ( FileObject fo, DataLoader.RecognizedFiles recognized ) throws IOException { return new DefaultDataObject (fo); } } /* Loader for shadows */ /** Public only for serialization. */ public static class ShadowLoader extends DataLoader { static final long serialVersionUID =-11013405787959120L; /* Representation class is DataShadow */ public ShadowLoader () { super (DataShadow.class); } protected void initialize () { setDisplayName (NbBundle.getBundle (DataLoaderPool.class).getString ("LBL_shadow_loader_display_name")); } protected DataObject handleFindDataObject ( FileObject fo, DataLoader.RecognizedFiles recognized ) throws IOException { if (fo.hasExt (DataShadow.SHADOW_EXTENSION)) { return DataShadow.deserialize (fo); } return null; } } } /* * Log * 42 Gandalf 1.41 1/15/00 Jaroslav Tulach SUID * 41 Gandalf 1.40 1/13/00 Jesse Glick All data loaders now * public for serialization. * 40 Gandalf 1.39 1/12/00 Ian Formanek NOI18N * 39 Gandalf 1.38 1/5/00 Jaroslav Tulach Find action for * DataFolders * 38 Gandalf 1.37 11/26/99 Patrik Knakal * 37 Gandalf 1.36 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 36 Gandalf 1.35 9/30/99 Jaroslav Tulach DataLoader is now * serializable. * 35 Gandalf 1.34 7/30/99 Jaroslav Tulach * 34 Gandalf 1.33 7/29/99 Jaroslav Tulach Removed GroupShadow. * 33 Gandalf 1.32 7/12/99 Jesse Glick Wrong index for * getDefaultFileLoader. * 32 Gandalf 1.31 6/10/99 Jaroslav Tulach Fires info * asynchronously. * 31 Gandalf 1.30 6/8/99 Ian Formanek Minor changes * 30 Gandalf 1.29 6/8/99 Ian Formanek ---- Package Change To * org.openide ---- * 29 Gandalf 1.28 5/24/99 Jaroslav Tulach XML data object in Open * API * 28 Gandalf 1.27 5/20/99 Jesse Glick [JavaDoc] * 27 Gandalf 1.26 5/13/99 Jaroslav Tulach Services changed to * tools. * 26 Gandalf 1.25 5/13/99 Jaroslav Tulach Services. * 25 Gandalf 1.24 5/11/99 Jaroslav Tulach * 24 Gandalf 1.23 5/4/99 Martin Ryzl Group Shadow added * 23 Gandalf 1.22 5/3/99 Jesse Glick [JavaDoc] and deleted * commented-out firstProducerOf(DataObject) as its possible utility * expired. * 22 Gandalf 1.21 5/2/99 Ian Formanek Added support for * recognition of files not recognized by any other loader * 21 Gandalf 1.20 4/27/99 Petr Hrebejk producersOf( Class ) fix * 20 Gandalf 1.19 4/26/99 Jesse Glick Bugfix -- operation * listeners did not work at all. * 19 Gandalf 1.18 3/31/99 Jaroslav Tulach Added * operationPostCreate to OperationListener * 18 Gandalf 1.17 3/30/99 Jaroslav Tulach New Package * 17 Gandalf 1.16 3/29/99 Martin Ryzl * 16 Gandalf 1.15 3/26/99 Martin Ryzl rolled back * 15 Gandalf 1.14 3/26/99 Martin Ryzl firstProducerOf * parameter type corrected * 14 Gandalf 1.13 3/24/99 Ian Formanek Removed obsoleted code * 13 Gandalf 1.12 3/22/99 Jaroslav Tulach FileSystemAction again. * 12 Gandalf 1.11 3/18/99 Jesse Glick [JavaDoc] * 11 Gandalf 1.10 3/9/99 Jesse Glick [JavaDoc] * 10 Gandalf 1.9 3/2/99 Jaroslav Tulach * 9 Gandalf 1.8 2/19/99 David Simonek menu related changes... * 8 Gandalf 1.7 2/8/99 Petr Hamernik OpenLocalExplorer * renamed to OpenLocalExplorerAction * 7 Gandalf 1.6 2/5/99 Jaroslav Tulach Changed new from * template action * 6 Gandalf 1.5 2/1/99 Jesse Glick [JavaDoc] * 5 Gandalf 1.4 1/21/99 Ales Novak * 4 Gandalf 1.3 1/20/99 Jaroslav Tulach * 3 Gandalf 1.2 1/15/99 Jaroslav Tulach * 2 Gandalf 1.1 1/6/99 Ales Novak * 1 Gandalf 1.0 1/5/99 Ian Formanek * $ */